package com.bluedot.www.core.service;


import com.bluedot.www.core.dao.Execute;
import com.bluedot.www.core.pojo.DO.Backup;
import com.bluedot.www.core.utils.*;
import com.bluedot.www.framework.mvc.annotation.MyService;

import java.io.*;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 备份 服务层
 * 删除是真删除（物理删除）
 * </p>
 *
 * @author DengHongWei
 * @since 2021-08-07
 */
@MyService
public class BackupService {

    private final static String BACKUP_MAPPER_ADDRESS = "com.bluedot.www.core.mapper.xml.BackupMapper.";

    private final static String BACKUP_NAME = "backup.sql";

    private final static String OSS_BACKUP_ADDRESS = "bluedot-a/";

    private final static String OSS_Real_ADDRESS = "bluedot/";

    private static Integer autoBackUpTime = 15;

    public static Object execute(String methodName, Object[] param) {
        BackupService backupService = new BackupService();
        Object result = null;
        switch (methodName) {
            case "autoBackUp":
                result = backupService.autoBackUp((Integer) param[0]);
                break;
            case "getTime":
                result = backupService.getTime();
                break;
            case "handBackUp":
                result = backupService.handBackUp((String) param[0]);
                break;
            case "handBackUpTolocal":
                result = backupService.handBackUpTolocal((String) param[0]);
                break;
            case "deleteSomeBackUpById":
                result = backupService.deleteSomeBackUpById((List<Long>) param[0]);
                break;
            case "recoverBackUpById":
                result = backupService.recoverBackUpById((Long) param[0]);
                break;
            case "countBackup":
                result = backupService.countBackup();
                break;
            default:
                throw new RuntimeException("Method NOT FOUND, method name: " + methodName);
        }
        return result;
    }

    /**
     * 自动备份
     *
     * @return boolean
     * @author DengHongWei
     * @date 2021/8/8 17:19
     */
    private boolean autoBackUp(Integer time) {
        autoBackUpTime = time;
        ThreadUtil.autoBackUpUtil(time);
        return true;
    }

    /**
     * 返回备份时间
     *
     * @return Integer
     * @author DengHongWei
     * @date 2021/8/8 17:19
     */
    private Integer getTime() {
        return autoBackUpTime;
    }

    /**
     * 手动备份
     *
     * @return boolean
     * @author DengHongWei
     * @date 2021/8/8 17:19
     */
    private boolean handBackUp(String fileName) {
        Timestamp currentTime = DbUtil.getCurrentTime();
        //数据库备份
        String location;
        HashMap<String, InputStream> myIns = new HashMap<>();
        try {
            location = OSSManageUtil.uploadFile(DataBaseUtil.dbBackUp(), fileName + ".sql", OSS_BACKUP_ADDRESS);
            if ("".equals(location)) {
                return false;
            }

            Map<String, InputStream> ins = OSSManageUtil.downloadAllByPath(OSS_Real_ADDRESS);
            //将前缀OSS_Real_ADDRESS改为OSS_BACKUP_ADDRESS实现数据备份至backup目录
            for (Map.Entry<String, InputStream> entry : ins.entrySet()) {
                String[] split = entry.getKey().split(OSS_Real_ADDRESS);
                //去掉backup.sql后缀，生成backup文件夹，同名文件夹好进行数据恢复
                myIns.put(location.substring(0, location.lastIndexOf(".")) + "/" + split[1], entry.getValue());
            }
            //生成备份文件夹
            OSSManageUtil.uploadAllFiles(myIns);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        Backup backup = new Backup(DbUtil.generateId(), fileName, location, currentTime, currentTime);
        Integer count = (Integer) Execute.execute(Execute.UPDATE_TYPE, BACKUP_MAPPER_ADDRESS + "insertBackUp", new Backup[]{backup});
        return count > 0;
    }

    /**
     * 将数据库备份至本地
     *
     * @param path 备份本地的路径
     * @return boolean
     * @author 蝉
     * @date 2021/9/13 19:47
     */
    private boolean handBackUpTolocal(String path) {
        InputStream in = DataBaseUtil.dbBackUp();
        try {
            //sql备份
            File file = new File(path);
            if (!file.exists()) {
                file.mkdirs();
            }
            FileOutputStream out = new FileOutputStream(path + "backup.sql");
            BufferedOutputStream bos = new BufferedOutputStream(out);
            BufferedInputStream bis = new BufferedInputStream(in);
            int len;
            byte[] bytes = new byte[1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            bos.flush();
            ExcelUtils.closeInStream(bis);
            ExcelUtils.closeInStream(in);
            ExcelUtils.closeOutStream(bos);
            ExcelUtils.closeOutStream(out);
            //文件备份
            Map<String, InputStream> ins = OSSManageUtil.downloadAllByPath(OSS_Real_ADDRESS);
            for (Map.Entry<String, InputStream> entry : ins.entrySet()) {
                String key = entry.getKey();
                int i = key.lastIndexOf("/");
                String path1 = path + "data/" + key.substring(0, i + 1);
                File file1 = new File(path1);
                if (!file1.exists()) {
                    file1.mkdirs();
                }
                FileOutputStream out1 = new FileOutputStream(path1 + key.substring(i + 1));
                BufferedOutputStream bos1 = new BufferedOutputStream(out1);
                InputStream value = entry.getValue();
                BufferedInputStream bis1 = new BufferedInputStream(value);
                int len1;
                byte[] bytes1 = new byte[1024];
                while ((len1 = bis1.read(bytes1)) != -1) {
                    bos1.write(bytes1, 0, len1);
                }
                bos1.flush();
                ExcelUtils.closeInStream(bis1);
                ExcelUtils.closeInStream(value);
                ExcelUtils.closeOutStream(bos1);
                ExcelUtils.closeOutStream(out1);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据备份id，删除多条备份记录
     * 寝室
     *
     * @param ids 备份id的list集合
     * @return boolean
     * @author DengHongWei
     * @date 2021/8/8 15:55
     */
    private boolean deleteSomeBackUpById(List<Long> ids) {
        boolean isDelete = false;
        for (Long id : ids) {
            List<Backup> backups = (List<Backup>) Execute.execute(Execute.QUERY_TYPE, BACKUP_MAPPER_ADDRESS + "findBackUpById", new Long[]{id});
            for (Backup backup : backups) {
                isDelete = OSSManageUtil.delete(backup.getLocation());
                OSSManageUtil.deleteAllByPath(backup.getLocation().substring(0, backup.getLocation().lastIndexOf(".")) + "/");
                Integer execute = (Integer) Execute.execute(Execute.UPDATE_TYPE, BACKUP_MAPPER_ADDRESS + "deleteBackUpById", new Long[]{id});
                if (execute != 1) {
                    return false;
                }
            }
        }
        return isDelete;
    }


    /**
     * 根据备份id，恢复备份记录
     *
     * @param id 备份日志id
     * @return boolean
     * @author DengHongWei
     * @date 2021/8/8 15:55
     */
    private boolean recoverBackUpById(Long id) {
        List<Backup> backups = (List<Backup>) Execute.execute(Execute.QUERY_TYPE, BACKUP_MAPPER_ADDRESS + "findBackUpById", new Long[]{id});
        for (Backup backup : backups) {
            //先删除目前运行路径的数据
            OSSManageUtil.deleteAllByPath(OSS_Real_ADDRESS);
            //再将数据备份的数据覆盖，相当于上传
            HashMap<String, InputStream> myIns = new HashMap<>();
            String location = backup.getLocation().substring(0, backup.getLocation().lastIndexOf("."));
            Map<String, InputStream> ins = null;
            try {
                ins = OSSManageUtil.downloadAllByPath(location + "/");
            } catch (IOException e) {
                e.printStackTrace();
            }
            //将前缀OSS_Real_ADDRESS改为OSS_BACKUP_ADDRESS实现数据备份至realRun目录
            for (Map.Entry<String, InputStream> entry : ins.entrySet()) {
                String[] split = entry.getKey().split(location + "/");
                //去掉backup.sql后缀，生成backup文件夹，同名文件夹好进行数据恢复
                myIns.put(OSS_Real_ADDRESS + split[1], entry.getValue());
            }
            //恢复OSS真正运行的文件
            try {
                OSSManageUtil.uploadAllFiles(myIns);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return DataBaseUtil.dbRecover(OSSManageUtil.getFileInputStream(backup.getLocation()));
        }
        return false;
    }


    /**
     * 获得笔迹特征数量
     *
     * @return java.lang.Integer 笔迹特征的数量
     * @author He Peng
     * @date 2021/9/8 20:56
     */
    private Integer countBackup() {
        List<Integer> tmp = (List<Integer>) Execute.execute(Execute.QUERY_TYPE,
                BACKUP_MAPPER_ADDRESS + "countBackup", null);
        if (tmp != null && tmp.size() > 0) {
            return tmp.get(0);
        }
        return 0;
    }
}
